/*
 * @(#)Game.java
 *
 * This work is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This work is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * Copyright (c) 2003 Per Cederberg. All rights reserved.
 */
package net.percederberg.tetris;

import GameControl.GameState;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;

/**
 * The Tetris game. This class controls all events in the game and
 * handles all the game logics. The game is started through user
 * interaction with the graphical game component provided by this 
 * class.
 *
 * @version  1.2
 * @author   Per Cederberg, per@percederberg.net
 */
public class Game extends Object {

    /**
     * The main square board. This board is used for the game itself.
     */
    public SquareBoard board = null;
    /**
     * The graphical game component. This component is created on the
     * first call to getComponent().
     */
    private GamePanel component = null;
    public GameState state;
    private boolean moveLock = true;

    /**
     * Creates a new Tetris game. The square board will be given
     * the default size of 10x20.
     */
    public Game() {
        this(10, 20);
    }

    /**
     * Creates a new Tetris game. The square board will be given
     * the specified size.
     *
     * @param width     the width of the square board (in positions)
     * @param height    the height of the square board (in positions)
     */
    public Game(int width, int height) {
        initGame(width, height);
    }

    private void initGame(int width, int height) {
        board = new SquareBoard(width, height);
        board.setMessage("Waiting");
    }

    /**
     * Returns a new component that draws the game.
     * 
     * @return the component that draws the game
     */
    public Component getComponent() {
        if (component == null) {
            component = new GamePanel();
        }
        return component;
    }

    /**
     * Handles a game start event. Both the main and preview square
     * boards will be reset, and all other game parameters will be
     * reset. Finally the game thread will be launched.
     */
    public void handleStart() {
        board.setMessage(null);
        board.matrix = state.matrix.clone();
        moveLock = false;
        board.update();
    }

    private void handlePause() {
        moveLock = true;
        board.setMessage("Waiting");
    }

    /**
     * Handles a keyboard event. This will result in different actions
     * being taken, depending on the key pressed. In some cases, other
     * events will be launched. This method is synchronized to avoid 
     * race conditions with other asynchronous events (timer and 
     * mouse).
     * 
     * @param e         the key event
     */
    private synchronized void handleKeyEvent(KeyEvent e) throws IOException {
        try {

            // Don't proceed if stopped or paused
            if (moveLock) {
                return;
            }

            // Handle remaining key events
            switch (e.getKeyCode()) {

                case KeyEvent.VK_LEFT:
                    Network.NetworkProtocol.SendCommand(new Control.Version(0));
                    break;
                case KeyEvent.VK_RIGHT:
                    Network.NetworkProtocol.SendCommand(new Control.Version(1));
                    break;
                case KeyEvent.VK_DOWN:
                    Network.NetworkProtocol.SendCommand(new Control.Version(2));
                    break;
                case KeyEvent.VK_UP:
                    Network.NetworkProtocol.SendCommand(new Control.Version(3));
                    break;
            }
        } catch (Exception ex) {
            handlePause();
            Network.NetworkProtocol.InitStateRequest();
        }
    }

    public void pMoveLeft() {
        state.moveLeft();
    }

    public void pMoveRight() {
        state.moveRight();
    }

    public void pStepDown() {
        state.moveDown();
    }

    public void pRotateCounterClockwise() {
        state.rotateCounterClockwise();
    }

    /**
     * A game panel component. Contains all the game components.
     */
    private class GamePanel extends Container {

        /**
         * The component size. If the component has been resized, that 
         * will be detected when the paint method executes. If this 
         * value is set to null, the component dimensions are unknown.
         */
        private Dimension size = null;

        /**
         * Creates a new game panel. All the components will
         * also be added to the panel.
         */
        public GamePanel() {
            super();
            initComponents();
        }

        /**
         * Paints the game component. This method is overridden from 
         * the default implementation in order to set the correct 
         * background color.
         * 
         * @param g     the graphics context to use
         */
        @Override
        public void paint(Graphics g) {
            Rectangle rect = g.getClipBounds();

            if (size == null || !size.equals(getSize())) {
                size = getSize();
            }
            g.setColor(getBackground());
            g.fillRect(rect.x, rect.y, rect.width, rect.height);
            super.paint(g);
        }

        /**
         * Initializes all the components, and places them in
         * the panel.
         */
        private void initComponents() {
            GridBagConstraints c;

            // Set layout manager and background
            setLayout(new GridBagLayout());
            setBackground(Configuration.getColor("background", "#d4d0c8"));

            // Add game board
            c = new GridBagConstraints();
            c.gridx = 0;
            c.gridy = 0;
            c.gridheight = 4;
            c.weightx = 1.0;
            c.weighty = 1.0;
            c.fill = GridBagConstraints.BOTH;
            this.add(board.getComponent(), c);

            // Add event handling            
            enableEvents(KeyEvent.KEY_EVENT_MASK);
            this.addKeyListener(new KeyAdapter() {

                @Override
                public void keyPressed(KeyEvent e) {
                    try {
                        handleKeyEvent(e);
                    } catch (IOException ex) {
                    }
                }
            });
        }
    }
}
